package pluginproxy

import (
	"errors"
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/grafana/grafana/pkg/util"
	"go-caipu/pkg/api/helper"
	"go-caipu/pkg/plugins"
	datasource "go-caipu/pkg/services/datasources"
	"go-caipu/pkg/util/proxyutil"
	"go-caipu/pkg/web"
	"net/http"
	"net/url"
)

type PluginProxy struct {
	pluginRoutes []*plugins.Route
	ctx          *gin.Context
	proxyPath    string
	matchedRoute *plugins.Route
	transport    *http.Transport
	dataSource   datasource.Service
	pluginId     string
}

func NewPluginProxy(routes []*plugins.Route, ctx *gin.Context, proxyPath string,
	transport *http.Transport, ds datasource.Service, pluginId string) (*PluginProxy, error) {
	return &PluginProxy{
		pluginRoutes: routes,
		ctx:          ctx,
		proxyPath:    proxyPath,
		transport:    transport,
		dataSource:   ds,
		pluginId:     pluginId,
	}, nil
}

// HandleRequest 代理发送请求
func (proxy *PluginProxy) HandleRequest() {
	//found route if there are any
	for _, route := range proxy.pluginRoutes {
		//method match
		if route.Method != "" && route.Method != "*" && route.Method != proxy.ctx.Request.Method {
			continue
		}

		t := web.NewTree()
		t.Add(route.Path, nil)
		_, params, isMatch := t.Match(proxy.proxyPath)

		if !isMatch {
			continue
		}

		if path, exists := params["*"]; exists {
			proxy.proxyPath = path
		} else {
			proxy.proxyPath = ""
		}

		proxy.matchedRoute = route
		break
	}

	if proxy.matchedRoute == nil {
		helper.JsonError(proxy.ctx, http.StatusNotFound, helper.Error(errors.New("plugin route match not found")))
		return
	}
	proxyErrorLogger := logger.New(
		"path", proxy.ctx.Request.URL.Path,
		"remote_addr", proxy.ctx.RemoteIP(),
		"referer", proxy.ctx.Request.Referer(),
	)
	reverseProxy := proxyutil.NewReverseProxy(
		proxyErrorLogger,
		proxy.director,
		proxyutil.WithTransport(proxy.transport),
	)
	fmt.Sprintf("待添加一个tracer 监控体系")
	reverseProxy.ServeHTTP(proxy.ctx.Writer, proxy.ctx.Request)
}
func (proxy *PluginProxy) director(req *http.Request) {
	ds, err := proxy.dataSource.GetDataSourceByType(req.Context(), proxy.pluginId)
	if err != nil {
		helper.JsonError(proxy.ctx, http.StatusInternalServerError, helper.Error(err))
		return
	}
	data := templateData{
		JsonData:       string(ds.JsonData),
		SecureJsonData: ds.SecureJsonData,
	}
	interpolatedURL, err := interpolateString(proxy.matchedRoute.URL, data)
	//Could not interpolate plugin route url
	if err != nil {
		helper.JsonError(proxy.ctx, http.StatusInternalServerError, helper.Error(err))
		return
	}
	targetURL, err := url.Parse(interpolatedURL)
	//Could not parse url
	if err != nil {
		helper.JsonError(proxy.ctx, http.StatusInternalServerError, helper.Error(err))
		return
	}
	req.URL.Scheme = targetURL.Scheme
	req.URL.Host = targetURL.Host
	req.Host = targetURL.Host
	req.URL.Path = util.JoinUrlFragments(targetURL.Path, proxy.proxyPath)
	// clear cookie headers
	req.Header.Del("Cookie")
	req.Header.Del("Set-Cookie")

	/*
	* 请求头Header 添加user信息
	 */
	//req.Header.Set("X-Grafana-Context", string(ctxJSON))
	//proxyutil.ApplyUserHeader(proxy.cfg.SendUserHeader, req, proxy.ctx.SignedInUser)

	if err := addHeaders(&req.Header, proxy.matchedRoute, data); err != nil {
		//Failed to render plugin headers
		helper.JsonError(proxy.ctx, http.StatusInternalServerError, helper.Error(err))
		return
	}

	if err := setBodyContent(req, proxy.matchedRoute, data); err != nil {
		logger.FromContext(req.Context()).Error("Failed to set plugin route body content", "error", err)
	}
}

//	type templateData struct {
//		JsonData       map[string]interface{}
//		SecureJsonData map[string]string
//	}
type templateData struct {
	JsonData       string
	SecureJsonData string
}
